package com.youcan.search.helper;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.solr.client.solrj.SolrResponse;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.SolrResponseBase;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.SolrParams;

import com.youcan.search.QueryOperation;
import com.youcan.search.QueryParams;
import com.youcan.search.SolrException;
import com.youcan.search.SolrServerFactory;


public class SolrHelper {

	private SolrServer solr;

	public SolrHelper(String coreName) {
		solr = new SolrServerFactory().getSolrServer(coreName);
	}

	/**
	 * @param data
	 * @param autoCommit
	 * @param callbacks
	 * @return
	 * @throws SolrException
	 */
	public boolean update(Map<String, Object> data, boolean autoCommit,
			Callback... callbacks) throws SolrException {
		SolrInputDocument doc = makeSolrInputDoc(data);
		try {
			UpdateResponse ur = solr.add(doc);
			if (autoCommit) {
				commit();
			}
			invokeCallbacks(ur, callbacks);
			return ur.getStatus() == 0;
		} catch (Exception e) {
			if (autoCommit) {
				rollback();
			}
			throw new SolrException(e);
		}
	}

	/**
	 * @param datas
	 * @param autoCommit
	 * @param callbacks
	 * @return
	 * @throws SolrException
	 */
	public boolean update(List<Map<String, Object>> datas, boolean autoCommit,
			Callback... callbacks) throws SolrException {
		List<SolrInputDocument> docs = new ArrayList<SolrInputDocument>(
				datas.size());
		for (Map<String, Object> data : datas) {
			docs.add(makeSolrInputDoc(data));
		}
		try {
			UpdateResponse ur = solr.add(docs);
			if (autoCommit) {
				commit();
			}
			invokeCallbacks(ur, callbacks);
			return ur.getStatus() == 0;
		} catch (Exception e) {
			if (autoCommit) {
				rollback();
			}
			throw new SolrException(e);
		}
	}

	/**
	 * @param id
	 * @return
	 * @throws SolrException
	 */
	public boolean remove(String id, boolean autoCommit, Callback... callbacks)
			throws SolrException {
		try {
			UpdateResponse ur = solr.deleteById(id);
			if (autoCommit) {
				commit();
			}
			invokeCallbacks(ur, callbacks);
			return ur.getStatus() == 0;
		} catch (Exception e) {
			if (autoCommit) {
				rollback();
			}
			throw new SolrException(e);
		}
	}

	/**
	 * @param ids
	 * @param autoCommit
	 * @param callbacks
	 * @return
	 * @throws SolrException
	 */
	public boolean remove(List<String> ids, boolean autoCommit,
			Callback... callbacks) throws SolrException {
		UpdateResponse ur;
		try {
			ur = solr.deleteById(ids);
			if (autoCommit) {
				commit();
			}
			invokeCallbacks(ur, callbacks);
			return ur.getStatus() == 0;
		} catch (Exception e) {
			if (autoCommit) {
				rollback();
			}
			throw new SolrException(e);
		}
	}

	/**
	 * solr检索，返回结果为匹配params里所有参数的记录。
	 * 
	 * @param params
	 * @param start
	 * @param rows
	 * @throws SolrServerException
	 */
	public List<Map<String, Object>> query(Map<String, String> params,
			int start, int rows, Callback... callbacks) throws SolrException {
		return query(params, QueryOperation.OR, start, rows, callbacks);
	}

	/**
	 * @param params
	 * @param qo
	 * @param start
	 * @param rows
	 * @return
	 * @throws SolrException
	 */
	public List<Map<String, Object>> query(Map<String, String> params,
			QueryOperation qo, int start, int rows, Callback... callbacks)
			throws SolrException {
		if (params == null || params.size() == 0) {
			return null;
		}
		StringBuilder queryString = new StringBuilder();
		for (String key : params.keySet()) {
			queryString.append(" ").append(qo).append(" ").append(key)
					.append(":").append(params.get(key));
		}
		return query("*,score",
				queryString.substring(2 + qo.toString().length()), "", start,
				rows, callbacks);
	}

	/**
	 * @param fl
	 *            需要返回的字段，以逗号分隔。
	 * @param params
	 *            查询参数，包含的参数需要被转换成字符串，可包含List<String>的列表参数，列表中的每项将会以
	 *            <code>qo</code>指定的逻辑操作连接。
	 * @param qo
	 *            查询条件之间的逻辑关系
	 * @param start
	 *            结果集的起始序号
	 * @param rows
	 *            总共需要返回的记录数
	 * @return
	 * @throws SolrException
	 */
	@SuppressWarnings("unchecked")
	public List<Map<String, Object>> query(String fl,
			Map<String, Object> params, QueryOperation qo, int start, int rows,
			Callback... callbacks) throws SolrException {
		if (params == null || params.size() == 0) {
			return null;
		}
		StringBuilder queryString = new StringBuilder();
		for (String key : params.keySet()) {
			Object obj = params.get(key);
			if (obj instanceof List) {
				for (String value : (List<String>) obj) {
					queryString.append(" ").append(qo).append(" ").append(key)
							.append(":").append(value);
				}
			} else {
				queryString.append(" ").append(qo).append(" ").append(key)
						.append(":").append(obj);
			}

		}
		return query(fl,
				queryString.substring(2 + qo.toString().length()), "", start,
				rows, callbacks);
	}

	/**
	 * solr检索
	 * 
	 * @param fl
	 *            指定返回那些字段内容，用逗号或空格分隔多个
	 * @param params
	 *            查询参数
	 * @param start
	 * @param rows
	 * @return
	 * @throws SolrException
	 */
	public List<Map<String, Object>> query(String fl,
			Map<String, String> params, int start, int rows,
			Callback... callbacks) throws SolrException {
		if (params == null || params.size() == 0) {
			return null;
		}
		StringBuffer queryString = new StringBuffer();
		for (String key : params.keySet()) {
			queryString.append(" AND ").append(key).append(":")
					.append(params.get(key));
		}
		return query(fl, queryString.substring(5), "", start, rows,
				callbacks);
	}

	/**
	 * @param qp
	 * @return
	 * @throws SolrException
	 */
	public List<Map<String, Object>> query(QueryParams qp,
			Callback... callbacks) throws SolrException {
		String query = qp.getQueryStr();
		if (query == null || query.trim().length() == 0) {
			return null;
		}
		Map<String, String> map = qp.toMap();
		SolrParams sp = new MapSolrParams(map);
		QueryResponse qr;
		try {
			qr = solr.query(sp);
		} catch (SolrServerException e) {
			throw new SolrException(e);
		}
		invokeCallbacks(qr, callbacks);
		if (qr.getStatus() == 0) {
			List<Map<String, Object>> rs = new ArrayList<Map<String, Object>>();
			SolrDocumentList list = qr.getResults();
			for (SolrDocument sd : list) {
				Map<String, Object> r = new HashMap<String, Object>();
				for (String key : sd.keySet()) {
					r.put(key, sd.get(key));
				}
				rs.add(r);
			}
			return rs;
		}
		return null;
	}

	/**
	 * @param q
	 * @param start
	 * @param rows
	 * @return
	 * @throws SolrServerException
	 */
	public List<Map<String, Object>> query(String fl, String q,
			String sort, int start, int rows, Callback... callbacks)
			throws SolrException {
		if (q == null || q.trim().length() == 0) {
			return null;
		}
		Map<String, String> map = new HashMap<String, String>();
		map.put("fl", fl);
		map.put("sort", sort);
		map.put("q", q);
		map.put("start", String.valueOf(start));
		map.put("rows", String.valueOf(rows));
		SolrParams sp = new MapSolrParams(map);
		QueryResponse qr;
		try {
			qr = solr.query(sp);
		} catch (SolrServerException e) {
			throw new SolrException(e);
		}
		invokeCallbacks(qr, callbacks);
		if (qr.getStatus() == 0) {
			List<Map<String, Object>> rs = new ArrayList<Map<String, Object>>();
			SolrDocumentList list = qr.getResults();
			for (SolrDocument sd : list) {
				Map<String, Object> r = new HashMap<String, Object>();
				for (String key : sd.keySet()) {
					r.put(key, sd.get(key));
				}
				rs.add(r);
			}
			return rs;
		}
		return null;
	}

	/**
	 * @return
	 * @throws SolrException
	 */
	public SolrResponse optimize() throws SolrException {
		try {
			return solr.optimize();
		} catch (Exception e) {
			throw new SolrException(e);
		}
	}

	/**
	 * @param waitFlush
	 * @param waitSearcher
	 * @return
	 * @throws SolrException
	 */
	public SolrResponse optimize(boolean waitFlush, boolean waitSearcher)
			throws SolrException {
		try {
			return solr.optimize(waitFlush, waitSearcher);
		} catch (Exception e) {
			throw new SolrException(e);
		}
	}

	/**
	 * @param waitFlush
	 * @param waitSearcher
	 * @param maxSegments
	 * @return
	 * @throws SolrException
	 */
	public SolrResponse optimize(boolean waitFlush, boolean waitSearcher,
			int maxSegments) throws SolrException {
		try {
			return solr.optimize(waitFlush, waitSearcher, maxSegments);
		} catch (Exception e) {
			throw new SolrException(e);
		}
	}

	/**
	 * @param waitFlush
	 * @param waitSearcher
	 * @return
	 * @throws SolrException
	 */
	public SolrResponse commit(boolean waitFlush, boolean waitSearcher)
			throws SolrException {
		try {
			return solr.commit(waitFlush, waitSearcher);
		} catch (Exception e) {
			throw new SolrException(e);
		}
	}

	/**
	 * @return
	 * @throws SolrException
	 */
	public SolrResponse commit() throws SolrException {
		try {
			return solr.commit();
		} catch (Exception e) {
			throw new SolrException(e);
		}
	}

	/**
	 * @return
	 * @throws SolrException
	 */
	public SolrResponse rollback() throws SolrException {
		try {
			return solr.rollback();
		} catch (Exception e) {
			throw new SolrException(e);
		}
	}

	/**
	 * @param data
	 * @return
	 */
	private SolrInputDocument makeSolrInputDoc(Map<String, Object> data) {
		SolrInputDocument doc = new SolrInputDocument();
		for (String key : data.keySet()) {
			if (data.get(key) instanceof List) {
				@SuppressWarnings("unchecked")
				List<Object> values = (List<Object>) data.get(key);
				for (Object value : values) {
					doc.addField(key, value);
				}
			} else {
				doc.addField(key, data.get(key));
			}
		}
		return doc;
	}

	private void invokeCallbacks(SolrResponseBase ur, Callback... callbacks) {
		if (callbacks != null && callbacks.length > 0) {
			for (Callback cb : callbacks) {
				cb.call(ur);
			}
		}
	}
}
